package com.zndroid.gridview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.ListAdapter;

import androidx.annotation.RequiresApi;

import com.zndroid.gridview.inner.drag.Global;
import com.zndroid.gridview.inner.drag.ICarrier;
import com.zndroid.gridview.inner.drag.IDrawer;
import com.zndroid.gridview.inner.drag.IModeType;
import com.zndroid.gridview.inner.drag.Item;
import com.zndroid.gridview.inner.drag.OnItemCapturedListener;
import com.zndroid.gridview.inner.drag.OnItemFixedClickListener;
import com.zndroid.gridview.inner.drag.OnItemMovedListener;
import com.zndroid.gridview.inner.drag.OnItemTrackListener;
import com.zndroid.gridview.inner.drag.OnItemTriggerListener;
import com.zndroid.gridview.inner.drag.ScrollRunner;
import com.zndroid.gridview.load.IImageDisplayEngine;

import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 可拖拽删除的 GridDroppableLayout（可以实现发朋友圈功能）
 *
 * 注意：如果您想在整个手机页面都可以进行拖拽，建议您根布局使用（后者适合做拖拽到指定区域删除功能）
 * {@link android.widget.LinearLayout} or {@link androidx.constraintlayout.widget.ConstraintLayout}
 * 因为需要与拖动的item父布局保证在同一层级，且根目录层级深度最多为 1（不包括根布局）
 * */
public class GridDroppableLayout extends GridView
    implements AdapterView.OnItemLongClickListener, AdapterView.OnItemClickListener, ICarrier
{
    /**全局的图片加载器(必须设置,否者不显示图片)*/
    private IImageDisplayEngine mImageLoader;
    /**模式 展示模式（默认）/触碰模式/长按模式*/
    private @IModeType int mode = IModeType.NONE;

    private AGridDragAdapter<? extends IImageBean> mAdapter;

    private OnScrollListener mOnScrollListener;
    private OnItemMovedListener mOnItemMovedListener;
    private OnItemClickListener mOnItemClickListener;
    private OnItemCapturedListener mOnItemCapturedListener;
    private OnItemLongClickListener mOnItemLongClickListener;
    private OnItemFixedClickListener mOnItemFixedClickListener;
    private OnItemTrackListener mOnItemTrackListener;
    private OnItemTriggerListener mOnItemTriggerListener;

    private int mScrollY;
    private int mFirstTop, mFirstLeft;
    private int mFirstVisibleFirstItem = -1;
    private float mLastMotionX, mLastMotionY;

    private Global mChildren;

    private View mDraggedView, mTriggerView;
    private int mDraggedIndex = -1;
    private final Rect mDraggedRect = new Rect();
    private final Rect mTriggerRect = new Rect();
    private int mColumnWidth, mRowHeight, mColumnsNum;
    private int mHorizontalSpacing;
    private int mVerticalSpacing;
    private int mDraggedPosition = INVALID_POSITION;

    private Rect mGridViewVisibleRect;
    private MotionEvent mCurrMotionEvent;

    private ScrollRunner mScrollRunner;
    private int mScrollSpeed = 750;
    private boolean mClipToPadding = false;
    private boolean mShouldMove = false;
    private boolean mUseSelector = false;
    private boolean mAutoOptimize = true;           //如果数据过多，当拖拽时，会自动滚动

    private Drawable mSelector;                     //item选择时高亮
    private Drawable mSpaceDrawable;
    private IDrawer mDrawer;                        //在现有的布局基础上绘制其他控件
    private boolean mDrawOnTop = false;             //在现有的布局基础上绘制其他控件，位置

    /**保证碰撞和不碰撞只触发一次回调*/
    private final AtomicBoolean isKeepCollision = new AtomicBoolean(false);
    private final AtomicBoolean isKeepNotCollision = new AtomicBoolean(false);

    public GridDroppableLayout(Context context) {
        this(context, null);
    }

    public GridDroppableLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GridDroppableLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public GridDroppableLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    public void setMode(@IModeType int mode) {
        this.mode = mode;
    }

    public void setImageLoader(IImageDisplayEngine imageLoader) {
        mImageLoader = imageLoader;
    }

    public IImageDisplayEngine getImageLoader() {
        return mImageLoader;
    }

    private void dispatchItemReleased() {
        if (mOnItemCapturedListener != null && !isFixedPosition(mDraggedPosition)) {
            mOnItemCapturedListener.onItemReleased(mDraggedView, mDraggedPosition);
        }

        if (mOnItemTriggerListener != null && !isFixedPosition(mDraggedPosition)) {
            mOnItemTriggerListener.onItemHoldReleased(mDraggedView, mDraggedPosition);

            //在碰撞过程中释放，即：在目标区域内释放
            if (isKeepCollision.get()) {
                mOnItemTriggerListener.onDragInReleased(mDraggedView, mDraggedPosition);
            }
        }

        //reset collision statue
        isKeepNotCollision.set(false);
        isKeepCollision.set(false);
    }

    public void setOnItemCapturedListener(OnItemCapturedListener listener) {
        mOnItemCapturedListener = listener;
    }

    @Override
    public void setOnItemClickListener(OnItemClickListener listener) {
        mOnItemClickListener = listener;
    }

    public void setOnItemFixedClickListener(OnItemFixedClickListener onItemFixedClickListener) {
        this.mOnItemFixedClickListener = onItemFixedClickListener;
    }

    @Override
    public void setOnScrollListener(OnScrollListener l) {
        mOnScrollListener = l;
    }

    public void setOnItemTrackListener(OnItemTrackListener onItemTrackListener) {
        this.mOnItemTrackListener = onItemTrackListener;
    }

    /**
     * When set, will change TOUCH mode to LONG_PRESS mode if the content of gridview can not scroll.
     * Set by default.
     *
     * @param autoOptimize true or false
     */
    public void setAutoOptimize(boolean autoOptimize) {
        mAutoOptimize = autoOptimize;
    }

    /**
     * set the number of pixels gridview scrolled per second.
     *
     * @param scrollSpeed speed
     */
    public void setScrollSpeed(int scrollSpeed) {
        mScrollSpeed = scrollSpeed;
    }

    public void setMax(int max) {
        if (max < 0) { throw new UnsupportedOperationException("max must >= 0");}

        mAdapter.setMax(max);
    }

    public void setDragAdapter(AGridDragAdapter<? extends IImageBean> adapter) {
        this.mAdapter = adapter;
        if (adapter != null) {
            mOnItemMovedListener = (OnItemMovedListener) adapter;
        } else {
            throw new UnsupportedOperationException("Your adapter should implements OnItemMovedListener for listening  item's swap action.");
        }

        super.setAdapter(mAdapter);
    }

    /**
     * Deprecated Use {@link #setDragAdapter(AGridDragAdapter)} instead.
     * */
    @Override
    @Deprecated
    public void setAdapter(ListAdapter adapter) {
        //ignore
        //deprecated
    }

    /**
     * If you want to draw something in gridview,just set a drawer.
     *
     * @param drawer    a drawer.
     * @param drawOnTop true if you want draw on the top of Gridview.
     * @see IDrawer#onDraw(Canvas, int, int)
     */
    public void setDrawer(IDrawer drawer, boolean drawOnTop) {
        this.mDrawer = drawer;
        mDrawOnTop = drawOnTop;
    }

    /**
     * Tells the GridView whether to use Selector.
     * Nonuse selector by default.
     *
     * @param enabled true if use selector.
     */
    public void setSelectorEnabled(boolean enabled) {
        if (enabled != mUseSelector) {
            mUseSelector = enabled;
            if (mUseSelector && mSelector != null) {
                setSelector(mSelector);
            }
            if (!mUseSelector) {
                setSelector(getSelector());
            }
        }

    }

    public boolean isSelectorEnabled() {
        return mUseSelector;
    }

    @Override
    public void setSelector(Drawable sel) {
        if (mUseSelector) {
            super.setSelector(sel);
        } else {
            mSelector = sel;
            if (mSpaceDrawable == null) {
                mSpaceDrawable = new ColorDrawable();
            }
            super.setSelector(mSpaceDrawable);
        }
    }

    @Override
    public void setHorizontalSpacing(int horizontalSpacing) {
        super.setHorizontalSpacing(horizontalSpacing);
        mHorizontalSpacing = horizontalSpacing;
    }

    @Override
    public void setNumColumns(int numColumns) {
        super.setNumColumns(numColumns);
        mColumnsNum = numColumns;
    }

    @Override
    public void setClipToPadding(boolean clipToPadding) {
        super.setClipToPadding(clipToPadding);
        mClipToPadding = clipToPadding;
    }

    @Override
    public void setVerticalSpacing(int verticalSpacing) {
        super.setVerticalSpacing(verticalSpacing);
        mVerticalSpacing = verticalSpacing;
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        if (!mDrawOnTop) {
            drawSomeThing(canvas);
        }
        super.dispatchDraw(canvas);
        if (mDrawOnTop) {
            drawSomeThing(canvas);
        }
    }

    private void drawSomeThing(Canvas canvas) {
        if (mDrawer != null) {
            canvas.save();
            mDrawer.onDraw(canvas, getWidth(), getHeight());
            canvas.restore();
        }
    }

    private void refreshChildren() {
        int childCount = getChildCount();
        clearAllChildren();
        for (int i = 0; i < childCount; i++) {
            addChild(i, super.getChildAt(i));
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        mGridViewVisibleRect = null;
        measureVisibleRect();
    }

    @Override
    protected void detachViewsFromParent(int start, int count) {
        super.detachViewsFromParent(start, count);
        if (start == 0) {
            for (int i = start; i < start + count; i++) {
                removeChild(0);
            }
        } else {
            start = mChildren.size() - 1;
            for (int i = start; i > start - count; i--) {
                removeChild(i);
            }
        }
    }

    @Override
    protected void attachViewToParent(View child, int index, ViewGroup.LayoutParams params) {
        super.attachViewToParent(child, index, params);
        addChild(index, child);
    }

    @Override
    protected void detachAllViewsFromParent() {
        super.detachAllViewsFromParent();
        clearAllChildren();
    }

    @Override
    public void onViewAdded(View child) {
        super.onViewAdded(child);
        int index = indexOfChild(child);
        addChild(index, child);
    }

    @Override
    public void onViewRemoved(View child) {
        super.onViewRemoved(child);
        removeChild(child);
    }

    @Override
    protected void layoutChildren() {
        super.layoutChildren();
        if (mDraggedView != null) {
            refreshChildren();
            View draggedView = super.getChildAt(mDraggedPosition - mFirstVisibleFirstItem);
            dispatchItemReleased();
            mDraggedView = draggedView;
            dispatchItemCaptured();
            correctDraggedViewLocation(0, 0);
        } else {
            refreshChildren();
        }
    }

    private void init(Context context) {
        setChildrenDrawingOrderEnabled(true);//设置支持重排序
        setOverScrollMode(OVER_SCROLL_NEVER);
        setSmoothScrollbarEnabled(true);

        //拦截事件
        super.setOnItemLongClickListener(this);
        super.setOnItemClickListener(this);
        super.setOnScrollListener(new OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                if (mOnScrollListener != null) {
                    mOnScrollListener.onScrollStateChanged(view, scrollState);
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                if (visibleItemCount != 0) {
                    mFirstVisibleFirstItem = firstVisibleItem;
                    getBasicValues(firstVisibleItem);
                    if (mOnScrollListener != null) {
                        mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
                    }
                }
            }
        });

        mScrollRunner = new ScrollRunner(this, new AccelerateDecelerateInterpolator());
        mChildren = new Global(this);
    }

    @Override
    public void onMove(int lastX, int lastY, int curX, int curY) {
        int deltaY = curY - lastY;
        mDraggedView.offsetTopAndBottom(deltaY);
        scrollListBy(deltaY);
        swapItemIfNeed(mCurrMotionEvent);
    }

    @Override
    public void onDone() {

    }

    @Override
    public void scrollListBy(int y) {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
            super.scrollListBy(y);
        } else {
            invokeMethod(this, "trackMotionScroll", new Object[]{-y, -y}, new Class[]{int.class, int.class});
        }
    }

    @Override
    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
        //固定桩不可触发长按操作
        boolean handled = false;
        if(isLongPressMode() && !isFixedPosition(position)) {
            //重新排序，防止被后面或者其他item遮挡，bringToFront方法无效的
            invalidate();

            //记录拖拽的对象
            recordDragViewIfNeeded(view, position);

            handled = true;
        }

        if (mOnItemLongClickListener != null) {
            boolean longClickReturn = mOnItemLongClickListener.onItemLongClick(parent, view, position, id);
            if (!handled) {
                handled = longClickReturn;
            }
        }

        return handled;
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        if (null != mOnItemFixedClickListener && isFixedPosition(position)) {
            mOnItemFixedClickListener.onFixedItemClicked(mAdapter.isAutoDismissFixPile(), position, mAdapter.getItem(position), mAdapter.list);
        }

        if (mOnItemClickListener != null) {
            mOnItemClickListener.onItemClick(parent, view, position, id);
        }
    }

    @Override
    public void setOnItemLongClickListener(OnItemLongClickListener listener) {
        mOnItemLongClickListener = listener;
    }

    private void swapItemIfNeed(MotionEvent ev) {
        if (ev == null || mDraggedView == null || isFixedPosition(mDraggedPosition)) {
            return;
        }

        measureVisibleRect();
        measureDraggedRect();

        final int realX = (int) (ev.getRawX() - mGridViewVisibleRect.left);
        final int realY = (int) (ev.getRawY() - mGridViewVisibleRect.top);
        int currDraggedPosition = pointToPosition(realX, realY);
        boolean intersect = isDraggedInGridView();

        int draggedViewPosition = INVALID_POSITION;
        if (currDraggedPosition != INVALID_POSITION) {
            if (intersect) {
                draggedViewPosition = currDraggedPosition;
            } else {
                draggedViewPosition = INVALID_POSITION;
            }
        }

        if (isFixedPosition(draggedViewPosition)) {
            draggedViewPosition = INVALID_POSITION;
        }

        if (draggedViewPosition != INVALID_POSITION) {
            int dragPosition = getDragPosition();
            if (draggedViewPosition != dragPosition) {
                measureDraggedRect();
                if (draggedViewPosition < dragPosition) {
                    for (int i = dragPosition - 1; i >= draggedViewPosition; i--) {
                        swapItem(i, i + 1);
                    }
                } else {
                    for (int i = dragPosition + 1; i <= draggedViewPosition; i++) {
                        swapItem(i, i - 1);
                    }
                }
                moveViewToPosition(draggedViewPosition, mDraggedView);
                mDraggedPosition = draggedViewPosition;
            }
        }
    }

    private void moveViewToPosition(int position, View view) {
        boolean removeResult = removeChild(view);
        addChild(getIndex(position), view);
    }

    private int getIndex(int position) {
        return position - mFirstVisibleFirstItem;
    }

    private void clearAllChildren() {
        mChildren.clear();
    }

    private void addChild(int index, View view) {
        if (index < 0) {
            index = mChildren.size();
        }
        mChildren.add(index, view);
    }

    private boolean removeChild(View view) {
        boolean result = false;
        int childSize = mChildren.size();
        for (int i = 0; i < childSize; i++) {
            Item child = mChildren.get(i);
            if (child.view == view) {
                result = mChildren.remove(child);
                break;
            }
        }
        return result;
    }

    private void removeChild(int index) {
        mChildren.remove(index);
    }

    public int getDragPosition() {
        return mDraggedPosition;
    }

    private void swapItem(int from, int to) {
        int fromIndex = from - getFirstVisiblePosition();
        Item fromChild = mChildren.get(fromIndex);
        final View fromView = fromChild.view;
        if (fromChild == null || fromView == null) {
            return;
        }

        fromChild.moveTo(from, to);
        moveViewToPosition(to, fromView);
        dispatchItemMoved(from, to);
        detachViewFromParent(fromView);
        super.attachViewToParent(fromView, to - getFirstVisiblePosition(), fromView.getLayoutParams());
    }

    private void dispatchItemMoved(int from, int to) {
        if (mOnItemMovedListener != null) {
            mOnItemMovedListener.onItemMoved(from, to);
        }
    }

    private void getBasicValues(int firstVisibleItem) {
        View firstChild = getChildAt(0);

        mFirstLeft = getListPaddingLeft();
        mFirstTop = firstChild.getTop();
        mColumnWidth = firstChild.getWidth();
        mRowHeight = firstChild.getHeight();
        int rowLine = firstVisibleItem / mColumnsNum;
        mScrollY = mFirstTop - rowLine * (mVerticalSpacing + mRowHeight);
    }

    private boolean isDraggedInGridView() {
        return mGridViewVisibleRect.intersects(mDraggedRect.left, mDraggedRect.top, mDraggedRect.right, mDraggedRect.bottom);
    }

    private void measureDraggedRect() {
        mDraggedView.getGlobalVisibleRect(mDraggedRect);
        int[] location = new int[2];
        mDraggedView.getLocationOnScreen(location);
        mDraggedRect.set(location[0], location[1], location[0] + mDraggedRect.width(), location[1] + mDraggedRect.height());
    }

    private void measureVisibleRect() {
        if (mGridViewVisibleRect == null) {
            mGridViewVisibleRect = new Rect();
            getGlobalVisibleRect(mGridViewVisibleRect);
            int[] location = new int[2];
            getLocationOnScreen(location);
            mGridViewVisibleRect.set(location[0], location[1], location[0] + mGridViewVisibleRect.width(),
                    location[1] + mGridViewVisibleRect.height());
        }
    }

    private int getMotionPosition() {
        final int realX = (int) (mCurrMotionEvent.getRawX() - mGridViewVisibleRect.left);
        final int realY = (int) (mCurrMotionEvent.getRawY() - mGridViewVisibleRect.top);
        return pointToPosition(realX, realY);
    }

    private void recordDragViewIfNeeded(View view, int position) {
        measureVisibleRect();
        if (view == null && position == -1) {
            int currDraggedPosition = getMotionPosition();
            if (currDraggedPosition != INVALID_POSITION) {
                recordDragViewIfNeeded(getChildAt(currDraggedPosition - mFirstVisibleFirstItem), currDraggedPosition);
            }
        } else {
            mDraggedPosition = position;
            mDraggedView = view;
            measureDraggedRect();
            mDraggedIndex = mDraggedPosition - mFirstVisibleFirstItem;
            dispatchItemCaptured();
            correctDraggedViewLocation(0, 0);
        }
    }

    private void dispatchItemCaptured() {
        if (mOnItemCapturedListener != null && !isFixedPosition(mDraggedPosition)) {
            mOnItemCapturedListener.onItemCaptured(mDraggedView, mDraggedPosition);
        }

        if (mOnItemTriggerListener != null && !isFixedPosition(mDraggedPosition)) {
            mOnItemTriggerListener.onItemHoldUp(mDraggedView, mDraggedPosition);
        }
    }

    /**
     * check drag view and trigger view relative location.
     * */
    private void correctTriggerBorder() {
        if (null == mTriggerView) {
            return;
        }

        mTriggerView.getGlobalVisibleRect(mTriggerRect);

//        Rect(left, top, right, bottom)这四个 参数 分别代表的意思是：
//        left  : 矩形左边的X坐标
//        top   : 矩形顶部的Y坐标
//        right : 矩形右边的X坐标
//        bottom: 矩形底部的Y坐标
        boolean isCollision = isCollisionWithRect(mDraggedRect.left, mDraggedRect.top, mDraggedRect.width(), mDraggedRect.height(),
                mTriggerRect.left, mTriggerRect.top, mTriggerRect.width(), mTriggerRect.height());

        if (null != mOnItemTriggerListener) {
            if (isCollision) {
                isKeepNotCollision.set(false);

                if (!isKeepCollision.getAndSet(true)) {
                    mOnItemTriggerListener.onDragIn(mDraggedView, mDraggedPosition);
                }
            } else {
                isKeepCollision.set(false);

                if (!isKeepNotCollision.getAndSet(true)) {
                    mOnItemTriggerListener.onDragOut(mDraggedView, mDraggedPosition);
                }
            }

        }
    }

    public boolean isCollisionWithRect(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) {
//      P（point） : drag view
//      T（target）: target view

//      图例如下
//
//        |<- w1 ->|
//        ┍────────┑┬
//        │        │
//        │    P   │h1
//        │        │
//        ┕────────┙┴
//
//        |<------   w2   ------->|
//        ┍───────────────────────┑┬
//        │          T            │h2
//        ┕───────────────────────┙┴
//
//
//         P T 相对位置
//                            ↑(y)
//                            |      P1
//                    P2      |
//                            |
//                -----------T|(x2,y2)------→(x)
//                            |
//                            |     P4
//                    P3      |
//                            |


        if (x1 >= x2 && x1 >= x2 + w2) {//P相对于T，在T右边，则继续判断T左上角的横坐标是否在P的左边（不碰撞）
            return false;
        } else if (x1 <= x2 && x1 + w1 <= x2) {//P相对于T，在T左边，则继续判断P右上角的横坐标是否在T的左边（不碰撞）
            return false;
        } else if (y1 >= y2 && y1 >= y2 + h2) {//P相对于T，在T下方，则继续判断T左下角的纵坐标是否在P的上边（不碰撞）
            return false;
        } else if (y1 <= y2 && y1 + h1 <= y2) {//P相对于T，在T上方，则继续判断P右下角的纵坐标是否仍然在T的上边（不碰撞）
            return false;
        }
        return true;
    }

    /**
     * Let our finger touch the center area of draggedView.
     */
    private void correctDraggedViewLocation(int deltaX, int deltaY) {
        if (mCurrMotionEvent == null) {
            return;
        }

        float motionX = mCurrMotionEvent.getRawX();
        float motionY = mCurrMotionEvent.getRawY();

        if (null != mOnItemTrackListener) {
            mOnItemTrackListener.onRawTrajectory(motionX, motionY);
            mOnItemTrackListener.onTrajectory(mCurrMotionEvent.getX(), mCurrMotionEvent.getY());
        }

        measureVisibleRect();
        deltaX = (int) (motionX - mGridViewVisibleRect.left - (mDraggedView.getLeft() + mColumnWidth / 2)) + deltaX;
        deltaY = (int) (motionY - mGridViewVisibleRect.top - (mDraggedView.getTop() + mRowHeight / 2)) + deltaY;
        if (!isFixedPosition(mDraggedPosition)) {
            mDraggedView.offsetLeftAndRight(deltaX);
            mDraggedView.offsetTopAndBottom(deltaY);
        }
    }

    public @IModeType int getMode() {
        return mode;
    }

    public boolean isNoneMode() {
        return mode == IModeType.NONE;
    }

    //长按模式
    public boolean isLongPressMode() {
        return mode == IModeType.LONG_PRESS;
    }

    //是否是固定桩
    private boolean isFixedPosition(int position) {
        if (position != INVALID_POSITION && mAdapter != null) {
            mOnItemMovedListener = (OnItemMovedListener) mAdapter;
            return mOnItemMovedListener.isFixed(position);
        }
        return false;
    }

    @Override
    public View getChildAt(int index) {
        int position = index;
        final int childCount = getChildCount();
        if (mDraggedView != null) {
            int dragIndex = mDraggedPosition - mFirstVisibleFirstItem;
            if (dragIndex == 0) {
                if (index == 0) {
                    position = 1;
                } else if (position == 1) {
                    position = 0;
                } else {
//                position = position;
                }

            } else if (dragIndex == childCount - 1) {
                if (childCount % mColumnsNum != 1) {
                    if (index == childCount - 1) {
                        position = index - 1;
                    } else if (index == childCount - 2) {
                        position = childCount - 1;
                    }
                }
            }
        }
        if (position >= getChildCount()) {
            position = getChildCount() - 1;
        }
        return super.getChildAt(position);
    }

    @Override
    public int getChildCount() {
        return super.getChildCount();
    }

    @Override
    public int pointToPosition(int x, int y) {
        if (mColumnWidth + mHorizontalSpacing <= 0 || mRowHeight + mVerticalSpacing == 0) {
            return INVALID_POSITION;
        }
        int m = (x - mFirstLeft) / (mColumnWidth + mHorizontalSpacing);
        int n = (y - mFirstTop) / (mRowHeight + mVerticalSpacing);
        int right = mFirstLeft + (m + 1) * (mColumnWidth + mHorizontalSpacing);
        int bottom = mFirstTop + (n + 1) * (mRowHeight + mVerticalSpacing) + mRowHeight;
        if (x > right || y > bottom || m >= mColumnsNum) {
            return INVALID_POSITION;
        } else {
            int result = n * mColumnsNum + m + mFirstVisibleFirstItem;
            result = result <= getLastVisiblePosition() ? result : INVALID_POSITION;
            return result;
        }
    }

    @Override
    protected int getChildDrawingOrder(int childCount, int i) {
        int order = i;
        if (mDraggedView != null) {
            mDraggedIndex = mDraggedPosition - mFirstVisibleFirstItem;
            if (i == mDraggedIndex) {
                order = childCount - 1;
            } else if (i == childCount - 1) {
                order = mDraggedIndex;
            }
        }
        return order;
    }

    public int[] getLeftAndTopForPosition(int position) {
        int[] lt = new int[2];
        int m = position % mColumnsNum;
        int n = position / mColumnsNum;
        int left = mFirstLeft + m * (mColumnWidth + mHorizontalSpacing);
        int top = mScrollY + n * (mRowHeight + mVerticalSpacing);
        lt[0] = left;
        lt[1] = top;
        return lt;
    }

    /**
     * detect whether the content of gridview can scroll down.
     *
     * @return true or false
     */
    public boolean canScrollDown() {
        final int threshold = mClipToPadding ? 0 : getListPaddingTop();
        if (mScrollY < threshold) {
            return true;
        }
        return false;
    }

    /**
     * detect whether the content of gridview can scroll up.
     *
     * @return true or false
     */
    public boolean canScrollUp() {
        final int threshold = mClipToPadding ? -mScrollY : getListPaddingBottom();
        return getTotalScrollY() > -threshold;
    }

    public int getTotalScrollY() {
        if (mAdapter == null) {
            return 0;
        }

        int row = (mAdapter.getCount() - 1) / mColumnsNum + 1;
        int total = row * mRowHeight + (row - 1) * mVerticalSpacing;
        return total - getHeight();
    }

    private Object invokeMethod(Object targetObject, String methodName, Object[] params, Class[] paramTypes) {
        Object returnObj = null;
        if (targetObject == null || TextUtils.isEmpty(methodName)) {
            return null;
        }
        Method method = null;
        for (Class cls = targetObject.getClass(); cls != Object.class; cls = cls.getSuperclass()) {
            try {
                method = cls.getDeclaredMethod(methodName, paramTypes);
                break;
            } catch (Exception e) {
                //ignore
            }
        }
        if (method != null) {
            method.setAccessible(true);
            try {
                returnObj = method.invoke(targetObject, params);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return returnObj;
    }

    //核心
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return handleTouchEvent(ev);
    }

    private boolean handleTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        mCurrMotionEvent = ev;
        boolean handled = false;
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mLastMotionX = ev.getRawX();
                mLastMotionY = ev.getRawY();
                mShouldMove = false;
                if (isTouchMode()) {
                    recordDragViewIfNeeded(null, -1);
                    invalidate();
                    if (mDraggedView != null) {
                        mDraggedView.setPressed(true);
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE:
                int deltaX = (int) (ev.getRawX() - mLastMotionX);
                int deltaY = (int) (ev.getRawY() - mLastMotionY);
                if (mDraggedView != null) {
                    handled = true;
                    // intercept the MotionEvent only when user is not scrolling
                    if (!mShouldMove) {
//                        if (Math.abs(deltaX) > mTouchSlop || Math.abs(deltaY) > mTouchSlop) {
//                            if (mDraggedView.isPressed()) {
//                                mDraggedView.setPressed(false);
//                            }
//                            mShouldMove = true;
//                        }
                        if (mDraggedView.isPressed()) {
                            mDraggedView.setPressed(false);
                        }
                        mShouldMove = true;
                    }
                    if (mShouldMove) {
                        correctDraggedViewLocation(deltaX, deltaY);
                        swapItemIfNeed(ev);
                        scrollIfNeeded();
                        correctTriggerBorder();
                    }
                    mLastMotionX = ev.getRawX();
                    mLastMotionY = ev.getRawY();
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (mDraggedView != null) {
                    releaseDraggedView();
                    mScrollRunner.cancel();
                    handled = true;
                }
//                int motionPosition = getMotionPosition();
                mDraggedView = null;
                mCurrMotionEvent = null;
//                super.onTouchEvent(ev);
                break;
            default:
                break;
        }
        if (isTouchMode()) {
            handled = true;
        }
        boolean result = handled ? handled : super.onTouchEvent(ev);
        return result;
    }

    public boolean isTouchMode() {
        if (canScrollDown() || canScrollUp()) {
            // if the content of gridview can not scroll,change mode to LONG_PRESS for better user experience.
            if (mAutoOptimize) {
                mode = IModeType.LONG_PRESS;
            }
        }
        return mode == IModeType.TOUCH;
    }

    private void scrollIfNeeded() {
        measureDraggedRect();
        measureVisibleRect();
        if (!isDraggedInGridView()) {
            mScrollRunner.cancel();
        } else if (mDraggedRect.top <= mGridViewVisibleRect.top) {
            boolean canScrollDown = canScrollDown();
            if (canScrollDown && !mScrollRunner.isRunning()) {
                int deltaY = mClipToPadding ? mScrollY : mScrollY - getListPaddingTop();
                final int duration = Math.abs(deltaY) * 1000 / mScrollSpeed;
                mScrollRunner.start(0, deltaY, duration);
            }
        } else if (mDraggedRect.bottom >= mGridViewVisibleRect.bottom) {
            boolean canScrollUp = canScrollUp();
            if (canScrollUp && !mScrollRunner.isRunning()) {
                int deltaY = mClipToPadding ? getTotalScrollY() + mScrollY : getTotalScrollY() + mScrollY + getListPaddingBottom();
                final int duration = Math.abs(deltaY) * 1000 / mScrollSpeed;
                mScrollRunner.start(0, deltaY, duration);
            }
        } else {
            mScrollRunner.cancel();
        }
    }

    private void releaseDraggedView() {
        int[] destination = getLeftAndTopForPosition(mDraggedPosition);
        int offsetX = destination[0] - mDraggedView.getLeft();
        int offsetY = destination[1] - mDraggedView.getTop();
        mDraggedView.offsetLeftAndRight(offsetX);
        mDraggedView.offsetTopAndBottom(offsetY);
        dispatchItemReleased();
        if (mDraggedView.isPressed()) {
            mDraggedView.setPressed(false);
        }
    }

    /**
     * 设置触发View，即：当选中的item和目标view产生/移除交集时触发
     * See Also:{@link #setOnItemTrackListener(OnItemTrackListener)}, will callback point(x, y) of current drag view.
     * */
    public void setTriggerView(View view, OnItemTriggerListener triggerListener) {
        this.mTriggerView = view;
        this.mOnItemTriggerListener = triggerListener;
    }
}
